/* Sentinel - IRC Statistical and Operator Services
** jupe.c - JupeServ backend functions
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: jupe.c,v 1.54 2004/04/01 22:29:25 wcampbel Exp $
*/

#include "stats.h"
#include "struct.h"
#include "proto.h"
#include "match.h"
#include "s_string.h"
#include "slog.h"
#include "mem.h"

#ifdef JUPESERV

dlink_list server_jupes;
dlink_list nick_jupes;

static void LoadOldJupes();

void LoadJupes()
{
  FILE *fp;
  int parc;
  int i;
  char *parv[MAXPARA];
  char buf[BUFSIZE];

  /* Clear the parv */
  for (i = 0; i < MAXPARA; i++)
    parv[i] = NULL;

  /* New format jupe configuration file */
  if ((fp = fopen(JUPEFILE, "r")) == NULL)
  {
    slog(DEFAULT_LOG, L_ERR, "Unable to load the jupe configuration file.");

    /* Check to see if there's an old style conf */
    LoadOldJupes();
    return ;
  }

  while (fgets(buf, BUFSIZE, fp))
  {
    parc = tabtoken(buf, parv);

    if (parc < 4)
    {
      slog(DEFAULT_LOG, L_WARNING, "Invalid entry in jupe configuration "
           "file");
      slog(DEFAULT_LOG, L_WARNING, "Ignoring...please correct");
      continue;
    }

    if (strchr(parv[0], '.') != NULL)
      AddServerJupe(parv[0], parv[1], atol(parv[2]), parv[3], 1);
    else
      AddNickJupe(parv[0], parv[1], atol(parv[2]), parv[3]);
  }
  fclose(fp);
}

void LoadOldJupes()
{
  FILE *fp;
  int parc;
  int i;
  char *parv[MAXPARA];
  char buf[BUFSIZE];

  /* Clear the parv */
  for (i = 0; i < MAXPARA; i++)
    parv[i] = NULL;

  if ((fp = fopen(OLDJUPEFILE, "r")) == NULL)
  {
    slog(DEFAULT_LOG, L_ERR, "Unable to load the old style jupe configuration "
         "file.");
    return ;
  }

  while (fgets(buf, BUFSIZE, fp))
  {
    parc = msgtoken(buf, parv);

    if (parc < 3)
    {
      slog(DEFAULT_LOG, L_WARNING, "Invalid entry in jupe configuration "
           "file");
      slog(DEFAULT_LOG, L_WARNING, "Ignoring...please correct");
      continue;
    }

    /* The old jupe format does not support nick jupes */
    AddServerJupe(parv[0], parv[1], atol(parv[2]), "No Reason", 1);
  }
  fclose(fp);

  /* We don't want this old style conf around anymore */
  SaveJupes();
  unlink(OLDJUPEFILE);
}

void SaveJupes()
{
  struct Jupe *j;
  dlink_node *dl;
  FILE *fp;

  /* I should zero out the jupe config file here */
  if ((server_jupes.head == NULL) && (nick_jupes.head == NULL))
  {
    truncate(JUPEFILE, 0);
    return ;
  }

  if ((fp = fopen(JUPEFILE, "w")) == NULL)
  {
    slog(DEFAULT_LOG, L_ERR, "Unable to open jupe configuration file for writing");
    return ;
  }

  DLINK_FOREACH(dl, server_jupes.head)
  {
    j = (struct Jupe *) dl->data;
    fprintf(fp, "%s\t%s\t%ld\t%s\n", j->jupe, j->oper, j->ts,
            j->reason);
  }
  DLINK_FOREACH(dl, nick_jupes.head)
  {
    j = (struct Jupe *) dl->data;
    fprintf(fp, "%s\t%s\t%ld\t%s\n", j->jupe, j->oper, j->ts,
            j->reason);
  }
  fclose(fp);
}

void AddServerJupe(char *jupe, char *oper, long ts, char *reason, int isboot)
{
  struct Jupe *j;
  struct Jupe *check = FindServerJupe(jupe);
  dlink_node *dl;

  /* Don't add a jupe if it's already there */
  if (check != NULL)
  {
    return ;
  }
  j = smalloc(sizeof(struct Jupe));
  dl = dlink_create();
  memcounts.jupe++;

  strlcpy(j->jupe, jupe, MAXHOST);
  strlcpy(j->oper, oper, MAXNICK + 1);
  j->ts = ts;
  strlcpy(j->reason, reason, LINE);

  dlink_add(j, dl, &server_jupes);

  /* Added the data structure Jupe record, now force it off the
  ** network and introduce our fake server in it's place
  */
  InitServerJupe(j, isboot);
}

void AddNickJupe(char *jupe, char *oper, long ts, char *reason)
{
  struct Jupe *j;
  struct Jupe *check = FindNickJupe(jupe);
  dlink_node *dl;

  /* Don't add a jupe if it's already there */
  if (check != NULL)
  {
    return ;
  }
  j = smalloc(sizeof(struct Jupe));
  dl = dlink_create();
  memcounts.jupe++;

  strlcpy(j->jupe, jupe, MAXHOST);
  strlcpy(j->oper, oper, MAXNICK + 1);
  j->ts = ts;
  strlcpy(j->reason, reason, LINE);

  dlink_add(j, dl, &nick_jupes);

  /* Added the data structure Jupe record, now force it off the
  ** network and introduce our fake server in it's place
  */
  InitNickJupe(j);
}

void InitServerJupe(struct Jupe *j, int isboot)
{
  struct Server *s = findserver(j->jupe);
  char jreason[LINE];
#ifdef SPLITSERV

  struct SpServer *spl = NULL;
  char *td = NULL;
#endif

  if (s || isboot)
  {
    sts(":%s SQUIT %s :JUPED by %s - %s", JupeServ.nick, j->jupe,
        j->oper, j->reason);
    DelServer(j->jupe);
  }

  snprintf(jreason, LINE, "{Jupitered} by %s - %s", j->oper, j->reason);
  sts(":%s SERVER %s 2 :%s", settings.name, j->jupe, jreason);

  AddServer(j->jupe, 1, 0, jreason, me.s);
#ifdef SPLITSERV

  spl = SpFindSplit(j->jupe);
  if (spl)
  {
    td = timediff(CurrentTime - spl->ts);
    slog(SPLIT_LOG, L_NOTICE, "End of Split: %s to %s: %s", j->jupe, settings.name,
         td);
    if (IsSPReport())
      notice(sp.join_chan, SplitServ.nick, "END OF SPLIT: %s to %s: %s",
             j->jupe, settings.name, td);
  }
  else
  {
    slog(SPLIT_LOG, L_NOTICE, "New Server: %s to %s", j->jupe, settings.name);
    if (!is_delayed)
    {
      if (IsSPReport())
        notice(sp.join_chan, SplitServ.nick, "CONNECT: %s to %s", j->jupe,
               settings.name);
    }
  }
  SpNewServer(j->jupe, settings.name);
  SpDelSplit(j->jupe);
#endif
}

void InitNickJupe(struct Jupe *j)
{
  char jreason[LINE];

  snprintf(jreason, LINE, "{Jupitered} by %s - %s", j->oper, j->reason);
  /* 2.10 isn't supported with jupe. */
#ifdef IRCD_BAHAMUT

  sts("NICK %s 1 1 +i jupe juped.nick %s 1 :%s", j->jupe, settings.name,
      jreason);
#else

  sts("NICK %s 1 1 +i jupe juped.nick %s :%s", j->jupe, settings.name,
      jreason);
#endif

  AddUser(j->jupe, "jupe", "juped.nick", me.s, jreason);
}

void DelServerJupe(char *jupe)
{
  struct Jupe *j;
  dlink_node *dl;

  DLINK_FOREACH(dl, server_jupes.head)
  {
    j = (struct Jupe *) dl->data;
    if (!strcasecmp(j->jupe, jupe))
    {
      dlink_delete(dl, &server_jupes);
      free(j);
      memcounts.jupe--;
      dlink_free(dl);

      /* Remove the juped server from the network */
      sts("SQUIT %s :Unjuped", jupe);

      DelServer(jupe);

      return ;
    }
  }

  slog(DEFAULT_LOG, L_WARNING, "Unable to delete jupe %s", jupe);
}

void DelNickJupe(char *jupe)
{
  struct Jupe *j;
  dlink_node *dl;

  DLINK_FOREACH(dl, nick_jupes.head)
  {
    j = (struct Jupe *) dl->data;
    if (!irccasecmp(j->jupe, jupe))
    {
      dlink_delete(dl, &nick_jupes);
      free(j);
      memcounts.jupe--;
      dlink_free(dl);

      /* Remove the juped server from the network */
      sts(":%s QUIT :Unjuped", jupe);

      DelUser(jupe);

      return ;
    }
  }

  slog(DEFAULT_LOG, L_WARNING, "Unable to delete jupe %s", jupe);
}

struct Jupe* FindServerJupe(char *jupe)
{
  struct Jupe *j;
  dlink_node *dl;

  debug("FindServerJupe(%s)", jupe);

  DLINK_FOREACH(dl, server_jupes.head)
  {
    j = (struct Jupe *) dl->data;
    if (!strcasecmp(jupe, j->jupe))
    {
      return j;
    }
  }

  return NULL;
}

struct Jupe* FindNickJupe(char *jupe)
{
  struct Jupe *j;
  dlink_node *dl;

  debug("FindNickJupe(%s)", jupe);

  DLINK_FOREACH(dl, nick_jupes.head)
  {
    j = (struct Jupe *) dl->data;
    if (!irccasecmp(jupe, j->jupe))
    {
      return j;
    }
  }

  return NULL;
}

void FlushJupeList()
{
  dlink_node *dl;
  dlink_node *tdl;
  struct Jupe *j;

  DLINK_FOREACH_SAFE(dl, tdl, server_jupes.head)
  {
    j = (struct Jupe *) dl->data;
    dlink_delete(dl, &server_jupes);
    free(j);
    memcounts.jupe--;
    dlink_free(dl);
  }
  DLINK_FOREACH_SAFE(dl, tdl, nick_jupes.head)
  {
    j = (struct Jupe *) dl->data;
    dlink_delete(dl, &server_jupes);
    free(j);
    memcounts.jupe--;
    dlink_free(dl);
  }
}

#endif
